package com.dreamana.ui
{
	import flash.display.Sprite;
	import flash.events.Event;
	
	/**
	 * Base Class of MONO UIComponents
	 * @author civet (dreamana.com)
	 * 
	 * Modified from MinimalComps
	 * (http://code.google.com/p/minimalcomps/, https://github.com/minimalcomps) 
	 */
	public class BaseComponent extends Sprite
	{
		protected var _width:Number = 0;
		protected var _height:Number = 0;
		protected var _enabled:Boolean = true;
		protected var _styleMap:Object = {};
		protected var _styleMapAsync:Object = {};
		
		
		public function BaseComponent()
		{
			
		}

		protected function init():void
		{
			addChildren();
			//invalidate();
			
			//draw now, make it measurable after initialization
			draw();
		}
		
		/**
		 * Overriden in subclasses to create child display objects.
		 */
		protected function addChildren():void
		{
			
		}
		
		/**
		 * Marks the component to be redrawn on the next frame.
		 */
		protected function invalidate():void
		{
			addEventListener(Event.ENTER_FRAME, onInvalidate);
		}
		
		///////////////////////////////////
		// public methods
		///////////////////////////////////		
		
		/**
		 * Abstract draw function.
		 */
		public function draw():void
		{
			
		}
		
		/**
		 * Moves the component to the specified position.
		 * @param xpos the x position to move the component
		 * @param ypos the y position to move the component
		 */
		public function setPosition(px:Number, py:Number):void
		{
			x = Math.round(px);
			y = Math.round(py);
		}
		
		/**
		 * Sets the size of the component.
		 * @param w The width of the component.
		 * @param h The height of the component.
		 */
		public function setSize(w:Number, h:Number):void
		{
			_width = w;
			_height = h;
			invalidate();
			//dispatchEvent(new Event(Event.RESIZE));
		}
		
		/**
		 * Overriden in subclasses to set style.
		 * @param style
		 * @param value
		 */		
		public function setStyle(style:String, value:Object):void
		{
			_styleMap[style] = value;
		}
		
		public function getStyle(style:String):Object
		{
			return _styleMap[style];
		}
		
		public function getStyleMap():Object
		{
			return _styleMap;
		}
		
		/**
		 * set style asynchronous
		 * @param style
		 * @param value
		 */
		public function setStyleAsync(style:String, value:Object):void
		{
			var skin:ISkin = value as ISkin;
			if(skin) {
				_styleMapAsync[style] = value;
				
				skin.addUpdateHandler(onSkinUpdate);
			}
			else {
				this.setStyle(style, value);
			}
		}
			
		///////////////////////////////////
		// event handlers
		///////////////////////////////////
		
		/**
		 * Called one frame after invalidate is called.
		 */
		protected function onInvalidate(event:Event):void
		{
			removeEventListener(Event.ENTER_FRAME, onInvalidate);
			draw();
		}
		
		private function onSkinUpdate(skin:ISkin):void
		{
			for(var style:String in _styleMapAsync) {
				if(_styleMapAsync[style] == skin) {
					this.setStyle(style, skin.getData());
				}
			}
		}
						
		///////////////////////////////////
		// getter/setters
		///////////////////////////////////
		
		/**
		 * Sets/gets the width of the component.
		 */
		override public function set width(w:Number):void
		{
			_width = w;
			invalidate();
			//dispatchEvent(new Event(Event.RESIZE));
		}
		override public function get width():Number
		{
			return _width;
		}
		
		/**
		 * Sets/gets the height of the component.
		 */
		override public function set height(h:Number):void
		{
			_height = h;
			invalidate();
			//dispatchEvent(new Event(Event.RESIZE));
		}
		override public function get height():Number
		{
			return _height;
		}
		
		/**
		 * Overrides the setter for x to always place the component on a whole pixel.
		 */
		override public function set x(value:Number):void
		{
			super.x = Math.round(value);
		}
		
		/**
		 * Overrides the setter for y to always place the component on a whole pixel.
		 */
		override public function set y(value:Number):void
		{
			super.y = Math.round(value);
		}
		
		/**
		 *  whether this component is enabled or not.
		 */
		public function set enabled(value:Boolean):void
		{
			_enabled = value;
			mouseEnabled = _enabled;
			mouseChildren = _enabled;
			tabEnabled = _enabled;
		}
		
		public function get enabled():Boolean
		{
			return _enabled;
		}
	}
}